<template>
  <t-dialog
    v-model:show="dialogVisible"
    persistent
    class="comment-drawer-dialog"
  >
    <t-card style="width:720px">
      <t-card-section style="border-bottom: 1px solid #f0f2f5">
        <div class="row justify-between full-width" style="">
          <!-- 评论回复 -->
          <div class="text-subtitle1" style="font-size: 18px">{{
            qLangComment.commentReply
          }}</div>
          <t-icon name="close" @click="onClickClose" style="font-size:20px" />
        </div>
      </t-card-section>

      <t-card-section class="q-pa-none">
        <!-- scroll加载分页时的loading效果需要加在滚动容器的外层 -->
        <div
          ref="dialogContentRef"
          class="comment-replies-dialog__body overflow-hidden flex flex-col justify-between relative"
          :style="{
            fontSize: '14px',
            height: $q.platform.is.desktop ? '75vh' : null,
          }"
        >
          <div
            ref="scrollAreaRef"
            class="dialog-body-scrollable overflow-y-auto flex-1 pb-5 relative w-full"
          >
            <!-- 评论详情 -->
            <div class="section-comment flex px-5 py-3">
              <!-- 左侧头像 -->
              <div class="left-avatar mr-2" style="">
                <UserAvatar
                  :image-url="dataItem.UserAvatar"
                  :data-user-id="dataItem.UserID"
                  class="cursor-pointer"
                  @mouseenter="onEnter"
                  @mouseleave="onLeave"
                />
              </div>

              <div
                class="flex-1 flex flex-col"
                style="width: calc(100% - 40px)"
              >
                <!-- 评论顶部信息 -->
                <div class="flex justify-between teld-color-gray mb-2">
                  <span class="teld-color-black font-semibold">
                    {{ userNameDisplay }}
                  </span>
                  <!-- <span :title="comment.CommentTime">{{ comment.CommentTimeRelative }}</span> -->
                </div>

                <!-- 评论内容行 -->
                <div class="comment-content mb-2">
                  <div
                    v-if="!comment.IsDeleted"
                    ref="commentContentRef"
                    v-html="decodeURIComponent(comment.Content)"
                    @click="onClickContent"
                  ></div>
                  <div v-else class="teld-color-gray">{{
                    qLangComment.commentIsDeleted
                  }}</div>
                </div>

                <!-- 图片预览 -->
                <t-image-preview
                  ref="imagePreviewRef"
                  v-model="currentImgSrc"
                  :src-list="previewSrcList"
                ></t-image-preview>

                <!-- 附件 -->
                <section
                  v-if="
                    !comment.IsDeleted &&
                    comment.Attachments &&
                    comment.Attachments.length
                  "
                  class="attachments-section -ml-0.5 mb-2 w-full"
                >
                  <div
                    v-for="attachment in comment.Attachments"
                    :key="attachment.ID"
                    class="attachment-file flex items-center hover:text-theme-color cursor-pointer"
                    @click="onClickAttachment(attachment)"
                  >
                    <!-- 文件类型图标 -->
                    <FileTypeIcon
                      :file-name="attachment.FileName"
                      class="w-5 h-5 mr-2"
                    />
                    <!-- 文件名 -->
                    <div
                      class="flex-1 truncate overflow-hidden"
                      :title="attachment.FileName"
                    >
                      {{ attachment.FileName }}
                    </div>
                  </div>
                </section>

                <!-- 评论底部行 -->
                <div class="flex justify-between">
                  <span :title="comment.CommentTime" class="teld-color-gray">
                    {{ comment.CommentTimeRelative }}
                  </span>
                  <div
                    class="comment-operations flex justify-end"
                    :class="[
                      disabled ? 'teld-color-lightgray' : 'teld-color-gray',
                    ]"
                  >
                    <!-- 点赞 -->
                    <div
                      class="comment-like flex cursor-pointer hover:text-theme-color leading-5"
                      :class="[disabled ? 'cursor-not-allowed' : '']"
                      @click="onClickLike"
                    >
                      <SvgHeartFilled v-show="comment.Liked" class="mr-1" />
                      <SvgHeart v-show="!comment.Liked" class="mr-1" />
                      <span class="">{{ comment.LikeCount || 0 }}</span>
                    </div>
                  </div>
                </div>
              </div>
            </div>

            <div class="gap h-4 w-full teld-bg-color-gray"></div>

            <!-- 回复列表 -->
            <div class="section-reply-list">
              <div
                class="reply-count py-3 px-5 border-b border-solid border-gray-100"
              >
                <span class="bg-white w-full">
                  <!-- {{ totalReplyCount }} 条回复 -->
                  {{ qLangComment.funcReplyCount(totalReplyCount) }}
                </span>
              </div>
              <ReplyItem
                v-for="reply in replyList"
                :key="reply.ID"
                :data-item="reply"
                :is-comment-deleted="comment.IsDeleted"
                class="reply-item-in-comment-detail px-5 pt-3"
                @reply="onReplyToReply"
                @delete="onDeleteReply(reply)"
              />
            </div>
          </div>

          <!-- 输入&发布回复 -->
          <draft-editor
            ref="draftEditorRef"
            :visible="!isLoading && visible"
            :show-avatar="false"
            :placeholder="inputPlaceholder"
            :offset-parent="$refs.dialogContentRef"
            class="with-shadow px-5 py-3 w-full"
            @comment="onReplyToComment"
          />

          <!-- 为了便于分辨要选择的人员，这里展示人员信息详情(Dialog因挂body所以单独处理) -->
          <PersonnelInfoPopup
            :model="userInfo"
            :user-id="userId"
            :target-element="targetElement"
            @change-visible="onPopupVisibleChange"
          ></PersonnelInfoPopup>

          <t-inner-loading :showing="isLoading">
            <t-spinner size="28px" color="primary" />
          </t-inner-loading>
        </div>
      </t-card-section>
    </t-card>
  </t-dialog>
</template>

<script>
// Components
import SvgHeart from '../../components/svg-icons/heart.vue';
import SvgHeartFilled from '../../components/svg-icons/heart-filled.vue';
import SvgStar from '../../components/svg-icons/star.vue';
import SvgStarFilled from '../../components/svg-icons/star-filled.vue';
import UserAvatar from '../../components/user-avatar/index.vue';
import FileTypeIcon from '../../components/file-type-icon/index.vue';
import ReplyItem from '../reply-item/index.vue';
import DraftEditor from '../draft-editor/index.vue';
import PersonnelInfoPopup from '../personnel-info-popup/index.vue';

// Utils
import { scrollPaging, saveAs, checkDirty } from '../../hooks';
import {
  GetRepliesList,
  saveReply,
  turnOnLike,
  cancelLike,
} from '../../hooks/api';

/**
 * 评论详情，回复列表
 */
export default {
  name: 'CommentRepliesDialog',
  inject: ['qLangComment', 'loginUserInfo', 'notificationLink'],
  props: {
    visible: {
      type: Boolean,
      default: false,
    },
    /**
     * Comment数据条目
     */
    dataItem: {
      type: Object,
      default: () => {},
    },
  },
  provide() {
    return {
      // 快捷选人popup定位用的外部容器，用于计算出现在@符号的上下左右位置
      getContainerForPersonnelSelectorPopup: () => {
        return this.$refs.scrollAreaRef;
      },
      showPersonnelInfo: this.showPersonnelInfo,
      hidePersonnelInfo: this.hidePersonnelInfo,
    };
  },
  emits: ['update:visible', 'reply', 'delete-reply'],
  components: {
    SvgHeart,
    SvgHeartFilled,
    SvgStar,
    SvgStarFilled,
    ReplyItem,
    DraftEditor,
    UserAvatar,
    FileTypeIcon,
    PersonnelInfoPopup,
  },
  data() {
    return {
      disabled: false, // TODO 是否在评论详情中增加删除功能？
      comment: {},
      replyList: [],
      isLoading: false,
      hasLoadedAll: false,
      pageIndex: 1,
      pageSize: 20,
      totalReplyCount: 0,
      // 评论中的图片预览用
      previewSrcList: [],
      currentImgSrc: '',
      /**
       * Dialog挂在Body，z-index问题，个人信息Popup被遮盖，所以需要Dialog内部处理
       */
      userInfo: null,
      userId: null,
      timerCloseUserInfo: null,
      targetElement: null,
      //
    };
  },
  watch: {
    visible(val) {
      if (val) {
        // dialog挂载时插槽内容没有加载，所以mountd时点不行
        setTimeout(() => {
          // 用延时确保插槽内容已挂载，再启动滚动监听
          const scrollArea = this.$refs.scrollAreaRef;
          if (scrollArea) {
            scrollArea.addEventListener('wheel', this.onScroll, true);
          }
          this.init();
        }, 300);
      } else {
        // 关闭后取消滚动监听
        const scrollArea = this.$refs.scrollAreaRef;
        scrollArea && scrollArea.removeEventListener('wheel', this.onScroll);
        this.reset();
      }
    },
  },
  computed: {
    dialogVisible: {
      get() {
        return this.visible;
      },
      set(val) {
        this.$emit('update:visible', val);
      },
    },
    isAuthor() {
      const loginUser = this.loginUserInfo;
      return loginUser.userId === this.comment.UserID;
    },
    userNameDisplay() {
      return this.isAuthor ? '我' : this.comment.UserName;
    },
    inputPlaceholder() {
      return `回复 ${this.userNameDisplay}`;
    },
  },
  methods: {
    async fetchPageData() {
      this.isLoading = true;
      try {
        const payload = {
          CommentId: this.comment.ID,
          TargetID: '',
          PageIndex: this.pageIndex,
          PageCount: this.pageSize,
          Sort: 0, // 回复排序0为倒序1为正序
        };
        const res = await GetRepliesList(payload);

        if (!res || res.state !== '1' || !res.data) {
          // 获取数据失败
          return;
        }

        // 成功获取数据后
        const { Items, TotalCount } = res.data;
        this.replyList = this.replyList.concat(Items);
        this.totalReplyCount = TotalCount;
        this.pageIndex += 1;
        this.hasLoadedAll = this.replyList.length >= TotalCount;
      } catch (error) {
        console.log(error);
      } finally {
        setTimeout(() => {
          this.isLoading = false;
        }, 500);
      }
    },
    init() {
      // Mock for UI development
      // this.replyList = this.dataItem.Replies;
      // setTimeout(() => {
      //   this.isLoading = false;
      // }, 500);

      this.comment = { ...this.dataItem };
      // 获取评论的回复列表
      this.fetchPageData();

      // commentContentRef 需要 nextTick
      this.$nextTick(() => {
        this.initImagePreview();
      });
    },
    initImagePreview() {
      const commentContentRef = this.$refs.commentContentRef;
      if (!commentContentRef) return;
      const elImages = commentContentRef.querySelectorAll('img');
      this.previewSrcList = [].map.call(elImages, elImage => {
        return elImage.src;
      });
    },
    reset() {
      this.replyList = [];
      this.totalReplyCount = 0;
      this.pageIndex = 1;
      this.currentImgSrc = '';
    },
    async onClickLike() {
      if (this.disabled) return;
      const isLike = (this.comment.Liked = !this.comment.Liked);
      const diff = this.comment.Liked ? 1 : -1;
      this.comment.LikeCount += diff;
      let succeeded = false;

      const payload = isLike
        ? { CommentID: this.comment.ID, TargetID: 0 }
        : { Id: this.comment.ID };
      // const serviceCode = isLike ? 'CMTS-Like' : 'CMTS-CancelLike';
      const updateLikeState = isLike ? turnOnLike : cancelLike;
      try {
        // const { data } = await this.$utils.sgApi.getDataAsync({
        //   url: this.$utils.common.getServiceUrl(serviceCode),
        //   data: isLike
        //     ? { comment: JSON.stringify(payload) }
        //     : { input: JSON.stringify(payload) },
        // });
        const res = await updateLikeState(payload);

        if (res.state !== '1') {
          return;
        }

        succeeded = true;
        // this.$TP.$message.success(isLike ? '已点赞' : '已取消点赞');
      } catch (error) {
        console.log(error);
      } finally {
        // 接口调用失败后，页面状态回滚
        if (!succeeded) {
          this.comment.Liked = !isLike;
          this.comment.LikeCount -= diff;
        }
      }
    },
    /**
     * 给回复发回复
     */
    onReplyToReply(data) {
      this.replyList.unshift(data);
      this.totalReplyCount += 1;
      this.$emit('reply');
    },
    /**
     * 给评论发回复
     */
    async onReplyToComment(draftInfo) {
      const loginUser = this.loginUserInfo;
      const { content, fileList, userIdList, onSucceed, onFail } = draftInfo;
      const newCommentReply = {
        // 接口必传字段：
        CommentID: this.comment.ID,
        Content: content,
        UserAvatar: loginUser.avatar,
        // 回复给
        // RespondedUserID: this.comment.UserID,
        // RespondedUserName: this.comment.UserName,
        Attachments: fileList.map(n => n.result),
        MentionedUsers: JSON.stringify(userIdList),
        Url: this.notificationLink(),
        UrlParams: JSON.stringify({
          // fpath: encodeURIComponent(this.pageUrl),
          showComment: true,
          commentId: this.comment.ID,
        }),
      };

      try {
        const res = await saveReply(newCommentReply);
        console.log('onReplyToComment', res);

        if (res.state !== '1' || !res.data) {
          onFail();
          return;
        }

        // mock new record, and override with real data
        Object.assign(
          newCommentReply,
          {
            ID: 'reply-reply-m-' + parseInt(Math.random() * 1000),
            ReplyTime: Date().toLocaleString(),
            UserID: loginUser.userId,
            UserName: loginUser.userName,
            Liked: false,
            LikeCount: 0,
            IsFavorite: false,
            FavoriteCount: 0,
            IsDeleted: false,
            CanCancel: true,
          },
          res.data,
        );

        // 调接口成功后清空输入框内容
        onSucceed({ focus: true });
        // 评论完将新添加评论展示出来
        this.replyList.unshift(newCommentReply);
        this.totalReplyCount += 1;
        this.$emit('reply');
      } catch (error) {
        console.log(error);
        onFail();
      }
    },
    onDeleteReply(replyItem) {
      this.totalReplyCount -= 1;
      this.$emit('delete-reply', replyItem);
    },
    onScroll(e) {
      const { deltaY } = e;
      console.log('onScroll', e);
      if (
        deltaY < 0 ||
        !this.replyList ||
        !this.replyList.length ||
        this.isLoading ||
        this.hasLoadedAll
      ) {
        // 滚向顶部时、没有数据时、正在加载数据时、及全部加载完时，滚动无需处理
        return;
      }
      requestAnimationFrame(this.scrollHanlder);
    },
    scrollHanlder() {
      const container = this.$refs.scrollAreaRef;
      const lastItem = container.querySelector(
        '.reply-item-in-comment-detail:last-child',
      );
      // 提前 300px 获取下一页数据
      scrollPaging(container, lastItem, 300, this.fetchPageData);
    },
    /**
     * MessageBox.confirm 即$confirm 不能用，公共样式里固定了超高 z-index，
     * 会导致使用过一次确认之后，再次打开 Dialog 被 el-message-box__wrapper 遮住
     * 因此，需要移除该元素，使得`.el-message-box__wrapper+.v-modal`选择器无法起作用即可
     * @deprecated
     */
    onMessageBoxConfirmed(done) {
      done();
      this.$nextTick(() => {
        const messageBoxWrapperList = document.querySelectorAll(
          '.el-message-box__wrapper',
        );
        messageBoxWrapperList.forEach(el => el.remove());
      });
    },
    /**
     * @deprecated
     */
    onBeforeCloseReplyDialog(done) {
      if (checkDirty(this.$refs.dialogContentRef)) {
        this.$TP
          .$confirm('如果选择确定关闭，已编辑的内容都将被丢弃。', '提示信息', {
            type: 'warning',
            closeOnClickModal: false,
          })
          .then(_ => {
            this.onMessageBoxConfirmed(done);
          })
          .catch(_ => {});
      } else {
        this.onMessageBoxConfirmed(done);
      }
    },
    onImageLoad(e) {
      e.target.click();
    },
    onClickContent(e) {
      const el = e.target;
      if (el.nodeName === 'IMG' && el.currentSrc) {
        this.currentImgSrc = el.currentSrc;
      }
    },
    handleDownloadFile(attachment) {
      const { AttachmentUrl, FileName } = attachment;
      saveAs({
        url: AttachmentUrl,
        fileName: FileName,
      });
    },
    onClickAttachment(attachment) {
      const ext = attachment.FileName.split('.').pop().toLowerCase();
      const previwer = this.$refs.imagePreviewRef;
      if (['png', 'jpg', 'jpeg', 'gif'].includes(ext) && previwer) {
        previwer.open(attachment.AttachmentUrl);
        return;
      }
      this.handleDownloadFile(attachment);
    },
    onPopupVisibleChange(close) {
      if (close) {
        this.userInfo = null;
        this.userId = null;
      } else {
        clearTimeout(this.timerCloseUserInfo);
        this.timerCloseUserInfo = null;
      }
    },
    showPersonnelInfo(args) {
      this.userInfo = null;
      this.userId = null;
      clearTimeout(this.timerCloseUserInfo);
      const { target, info } = args;
      this.targetElement = target;
      this.$nextTick(() => {
        this.userInfo = info ? { ...info } : null;
        this.userId = target.dataset.userId;
      });
    },
    hidePersonnelInfo() {
      this.timerCloseUserInfo = setTimeout(() => {
        this.userInfo = null;
        this.userId = null;
      }, 500);
    },
    onEnter(e) {
      // this.showPersonnelInfo({ target: e.target });
    },
    onLeave(e) {
      this.hidePersonnelInfo();
    },
    // ---- 与原组件不兼容的地方 ----
    // 废弃：onBeforeCloseReplyDialog onMessageBoxConfirmed
    onClickClose() {
      // check dirty before close
      const isDirty = checkDirty(this.$refs.dialogContentRef);
      console.log('isDirty = ', isDirty);
      if (isDirty) {
        this.$q
          .dialog({
            title: this.qLangComment.confirmation,
            message: this.qLangComment.closeConfirmation,
            cancel: true,
            persistent: true,
          })
          .onOk(() => {
            console.log('>>>> OK');
            this.dialogVisible = false;
          })
          .onCancel(() => {
            console.log('>>>> Cancel');
          });
        return;
      }
      this.dialogVisible = false;
    },
  },
};
</script>

<!-- 注意区分是否 scoped 样式 -->
<style lang="scss"></style>
